<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>白屏/空白块检测可视化 · Offline</title>
  <style>
    :root { --bg:#0b0f13; --panel:#12171d; --panel-2:#151b22; --text:#e7edf3; --muted:#92a1b1; --accent:#4aa9ff; --bad:#ff6b6b; }
    *{box-sizing:border-box}
    body{margin:0;font-family:system-ui,-apple-system,Segoe UI,Roboto,"PingFang SC","Hiragino Sans GB","Microsoft YaHei";background:#0b0f13;color:var(--text)}
    header{padding:16px 20px;display:flex;justify-content:space-between;align-items:center;border-bottom:1px solid #1c2530;background:#0f141a}
    header h1{margin:0;font-size:18px;font-weight:600}

    .wrap{display:grid;grid-template-columns:380px 1fr;gap:16px;padding:16px}
    .card{background:var(--panel);border:1px solid #1b232d;border-radius:12px}
    .card h2{margin:0;padding:12px 14px;font-size:14px;border-bottom:1px solid #1b232d;color:#cfe6ff}
    .card .body{padding:12px}

    textarea{width:100%;min-height:180px;resize:vertical;background:#0e1319;color:var(--text);border:1px solid #1b232d;border-radius:8px;padding:10px;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12px}
    .btns{display:flex;gap:8px;margin-top:10px;flex-wrap:wrap}
    button{padding:8px 12px;background:#111923;border:1px solid #253343;color:var(--text);border-radius:8px;cursor:pointer;font-size:13px}
    button.primary{background:#1b67ff;border-color:#2a70ff}

    /* Stage */
    .stage{display:flex;flex-direction:column;gap:12px}
    .phone{position:relative;width:clamp(320px,38vw,460px);aspect-ratio:9/19.5;margin:12px auto;background:#0b0f13;border-radius:24px;border:1px solid #223140;box-shadow:inset 0 0 0 6px #0b0f13}
    .screen{position:absolute;inset:10px;border-radius:16px;overflow:hidden;background:#0d1218}
    .screen-inner{position:absolute;inset:0;transform-origin:top left}

    .region{position:absolute;outline:2px dashed rgba(255,255,255,.8);border-radius:6px}
    .region .label{position:absolute;left:0;top:-20px;font-size:11px;padding:2px 6px;border-radius:4px;background:rgba(0,0,0,.55)}

    .stats{display:grid;grid-template-columns:repeat(2,1fr);gap:8px}
    .stat{background:#0e141a;border:1px solid #1b232d;border-radius:8px;padding:8px}
    .stat b{display:block;font-size:11px;color:var(--muted);margin-bottom:4px}
    .stat span{font-size:14px;font-weight:700}

    .table{width:100%;border-collapse:collapse;font-size:12px}
    .table th,.table td{border-bottom:1px solid #1b232d;padding:6px;text-align:left}
    .table th{color:#bcd3ea;font-weight:600}

    .hint{font-size:12px;color:var(--muted)}
    .error{color:var(--bad);font-size:12px;white-space:pre-wrap}
  </style>
</head>
<body>
  <header>
    <h1>白屏/空白块检测可视化</h1>
    <div class="hint">离线工具 · 本地执行</div>
  </header>

  <main class="wrap">
    <section class="card">
      <h2>输入 DetectionResult JSON</h2>
      <div class="body">
        <textarea id="jsonInput" placeholder='{"hasBlankScreen":true, ...}'></textarea>
        <div class="btns">
          <button class="primary" id="renderBtn">解析并渲染</button>
          <button id="sampleBtn">填充示例</button>
        </div>
        <div id="err" class="error"></div>
      </div>
    </section>

    <section class="card stage">
      <h2>可视化结果</h2>
      <div class="body">
        <div class="phone" id="phone">
          <div class="screen" id="screen">
            <div class="screen-inner" id="screenInner"></div>
          </div>
        </div>
        <div id="stats" class="stats"></div>
        <h3 style="font-size:13px;margin:8px 0;color:#cfe6ff;">检测区域</h3>
        <table class="table" id="regionsTable">
          <thead>
            <tr>
              <th>#</th>
              <th>类型</th>
              <th>位置/尺寸</th>
              <th>主色</th>
              <th>描述</th>
            </tr>
          </thead>
          <tbody></tbody>
        </table>
      </div>
    </section>
  </main>

  <script>
    const $=(s,e=document)=>e.querySelector(s); const $$=(s,e=document)=>Array.from(e.querySelectorAll(s));
    const jsonInput=$('#jsonInput'); const renderBtn=$('#renderBtn'); const sampleBtn=$('#sampleBtn'); const errBox=$('#err');
    const phone=$('#phone'); const screen=$('#screen'); const inner=$('#screenInner'); const statsBox=$('#stats'); const regionsTableBody=$('#regionsTable tbody');

    let current=null; let screenW=0, screenH=0;

    function rgbToHex({r,g,b}){const h=v=>v.toString(16).padStart(2,'0');return `#${h(r)}${h(g)}${h(b)}`.toUpperCase()}
    function blankTypeToText(t){return {0:'WHITE',1:'BLACK',2:'SOLID_COLOR',3:'LOADING_FAILURE',4:'COMPONENT_ERROR'}[t]||t}

    function computeExtent(regs){let mx=0,my=0;for(const r of regs||[]){mx=Math.max(mx,(r.bounds?.x||0)+(r.bounds?.width||0));my=Math.max(my,(r.bounds?.y||0)+(r.bounds?.height||0));}return {width:Math.max(1,mx),height:Math.max(1,my)}}

    function drawPhone(){ if(!current) return; $$('.region', inner).forEach(n=>n.remove());
      // Fit inner to screen viewport
      const rect=screen.getBoundingClientRect();
      const k=Math.min(rect.width/screenW, rect.height/screenH);
      const viewW=screenW*k, viewH=screenH*k; const offX=(rect.width-viewW)/2, offY=(rect.height-viewH)/2;
      inner.style.width=screenW+'px'; inner.style.height=screenH+'px';
      inner.style.transform=`translate(${offX}px, ${offY}px) scale(${k})`;

      // Regions positioned in screen coordinates
      (current.blankRegions||[]).forEach((r,i)=>{
        const {x=0,y=0,width=0,height=0}=r.bounds||{}; const dom=document.createElement('div');
        dom.className='region'; dom.style.left=x+'px'; dom.style.top=y+'px'; dom.style.width=width+'px'; dom.style.height=height+'px';
        const c=r.dominantColor||{r:255,g:255,b:255,a:255}; dom.style.background=`rgba(${c.r},${c.g},${c.b},0.38)`;
        const lab=document.createElement('div'); lab.className='label'; lab.textContent=`#${i+1} ${blankTypeToText(r.type)}`; dom.appendChild(lab);
        inner.appendChild(dom);
      });
    }

    function render(){ errBox.textContent=''; let data; try{ data=JSON.parse(jsonInput.value); }catch(e){ errBox.textContent='JSON解析失败: '+e.message; return; }
      current=data||{}; const ext=computeExtent(current.blankRegions||[]); screenW=ext.width; screenH=ext.height; drawPhone();
      const s=current.stats||{}; statsBox.innerHTML=`
        <div class="stat"><b>白块数量</b><span>${s.totalWhiteRegions||0}</span></div>
        <div class="stat"><b>黑块数量</b><span>${s.totalBlackRegions||0}</span></div>
        <div class="stat"><b>纯色块数量</b><span>${s.totalSolidColorRegions||0}</span></div>
        <div class="stat"><b>最大区域面积</b><span>${s.largestRegionArea||0}</span></div>`;

      regionsTableBody.innerHTML=(current.blankRegions||[]).map((r,i)=>{
        const hex=rgbToHex(r.dominantColor||{r:255,g:255,b:255});
        const b=r.bounds||{x:0,y:0,width:0,height:0};
        return `<tr><td>${i+1}</td><td>${blankTypeToText(r.type)}</td><td>(${b.x},${b.y}) / ${b.width}×${b.height}</td><td><span style="display:inline-block;width:16px;height:16px;background:${hex};margin-right:4px;border:1px solid rgba(255,255,255,.35);"></span>${hex}</td><td>${r.description||''}</td></tr>`
      }).join('');
    }

    sampleBtn.addEventListener('click',()=>{
      const sample={"hasBlankScreen":true,"overallBlankRatio":0.11715452847805789,"summary":"Detected 2 rectangular blank regions (11% of screen), 1 dominant colors","blankRegions":[{"bounds":{"x":656,"y":1584,"width":544,"height":384},"type":0,"confidence":0.9803730085784313,"uniformity":0.9803730085784313,"description":"White rectangular region","dominantColor":{"r":245,"g":245,"b":245,"a":255},"dominantColorHSV":{"h":0,"s":0,"v":96.07843137254902,"a":255}},{"bounds":{"x":656,"y":2208,"width":544,"height":368},"type":0,"confidence":0.982636668797954,"uniformity":0.982636668797954,"description":"White rectangular region","dominantColor":{"r":245,"g":245,"b":245,"a":255},"dominantColorHSV":{"h":0,"s":0,"v":96.07843137254902,"a":255}}],"stats":{"totalWhiteRegions":2,"totalBlackRegions":0,"totalSolidColorRegions":0,"largestRegionArea":204796,"averageRegionSize":200756,"dominantColors":[{"r":245,"g":245,"b":245,"a":255}],"dominantColorsHSV":[{"h":0,"s":0,"v":96.07843137254902,"a":255}]}};
      jsonInput.value=JSON.stringify(sample,null,2); render();
    });

    renderBtn.addEventListener('click',render);
    window.addEventListener('resize', drawPhone);
  </script>
</body>
</html>
